﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Security.Principal;
using System.Text;

namespace Sharp.Infrastructure.Network
{
    public class NetHelper
    {
        public NetHelper()
        {
            TimeOut = 100000;
            ProxyInt = 8080;
            ProxyHost = string.Empty;
            UserAgent = "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)";
        }
        public NetHelper(DateTime? modifiedSince) : this()
        {
            ModifiedSince = modifiedSince;
        }
        #region 属性

        /// <summary>
        /// 设置UserAgent
        /// </summary>
        public string UserAgent { get; set; }

        /// <summary>
        /// 设置代理服务器
        /// </summary>
        public string ProxyHost { get; set; }

        /// <summary>
        /// 设置代理服务器端口
        /// </summary>
        public int ProxyInt { get; set; }

        /// <summary>
        /// 设置默认超时时间
        /// </summary>
        public int TimeOut { get; set; }

        /// <summary>
        /// 网页编码
        /// </summary>
        public string EncodingString
        {
            get;
            set;
        }
        public DateTime? ModifiedSince { get; set; }
        #endregion

        public RemoteRes Get(string uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException(nameof(uri));
            }
            try
            {
                Uri _uri = new Uri(uri);
                return Get(_uri, string.Concat("http://", _uri.Host));
            }
            catch (UriFormatException)
            {
                throw new UriFormatException(uri + "格式无效");
            }
            catch (Exception)
            {
                throw;
            }
        }

        public RemoteRes Get(string uri, string referer)
        {
            Uri _uri = new Uri(uri);
            return Get(_uri, referer);
        }
        public RemoteRes Get(Uri uri)
        {
            return Get(uri, string.Concat("http://", uri.Host));
        }
        public RemoteRes Get(Uri uri, string referer)
        {
            RemoteRes info = new RemoteRes();

            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.Accept = "text/html, application/xhtml+xml, application/xml;q=0.9,*/*;q=0.8";
            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            request.ImpersonationLevel = TokenImpersonationLevel.Impersonation;
            request.Timeout = this.TimeOut;
            request.UserAgent = this.UserAgent;
            request.Method = "GET";
            request.Host = uri.Host;
            if (ModifiedSince.HasValue)
                request.IfModifiedSince = ModifiedSince.Value;
            request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
            request.Headers[HttpRequestHeader.AcceptLanguage] = "zh-CN";
            request.Headers.Add("X_FORWARDED_FOR", "8.8.8.8");
            try { request.Referer = referer; }
            catch
            {
                // ignored
            }
            request.AllowAutoRedirect = true;
            request.ServicePoint.Expect100Continue = false;
            //HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
            //request.CachePolicy = noCachePolicy;

            if (this.ProxyHost.Length > 0)
            {
                request.Proxy = new WebProxy(this.ProxyHost, this.ProxyInt);
            }
            else
            {
                request.Proxy = null;
            }


            HttpWebResponse response = null;
            Stream responseStream = null;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
 
                if (response.StatusCode == HttpStatusCode.NotModified)
                {
                    info.StatusCode = response.StatusCode;
                    return info;
                }
                info.LastModified = response.LastModified;

                if (!response.ContentType.ToLower().StartsWith("text/"))
                {
                    return GetRemoteResource(uri.ToString());
                }

                if (request.CookieContainer != null)
                {
                    response.Cookies = request.CookieContainer.GetCookies(request.RequestUri);
                }

                responseStream = response.GetResponseStream();

                string webCharSet = response.CharacterSet ?? "";
                Encoding encoding = null;
                if (string.IsNullOrEmpty(this.EncodingString) == false)
                {
                    encoding = System.Text.Encoding.GetEncoding(this.EncodingString);
                }
                if (encoding == null && webCharSet.IsNullOrEmpty())
                {
                    throw new NotSupportedException("网页编码无法获取。");
                }

                try //用来检测webCharSet是否符合标准
                {
                    encoding = Encoding.GetEncoding(webCharSet);
                }
                catch
                {
                    encoding = null;
                    webCharSet = "error";
                }

                if ((webCharSet.StartsWith("iso", StringComparison.CurrentCultureIgnoreCase)) || webCharSet == "error")
                {
                    byte[] webbytes = MyConvert.StreamToBytes(responseStream);
                    ////列出可能的几种编码
                    string[] encodings = Sharp.Infrastructure.StringEncode.StringEncode.GetEncodings(MyConvert.BytesToStream(webbytes));
                    encoding = GetEncoding(encodings);
                    info.Html = encoding.GetString(webbytes);
                    info.Charset = encoding.EncodingName;
                }
                else
                {
                    if (encoding == null)
                    {
                        encoding = Encoding.GetEncoding(webCharSet);
                    }
                    using (StreamReader sr = new StreamReader(responseStream, encoding))
                    {
                        info.Html = sr.ReadToEnd();
                    }
                }

                info.ContentType = response.ContentType;
                info.StatusCode = response.StatusCode;
            }
            catch (WebException we)
            {
                if (we.Response != null)
                {
                    info.StatusCode = (we.Response as HttpWebResponse).StatusCode;
                }
                else
                {
                    info.StatusCode = HttpStatusCode.ServiceUnavailable;
                }
                if (info.StatusCode == HttpStatusCode.NotModified)
                {
                    info.Code = we.Message;
                }
                else
                {
                    info.Code = "错误:" + we.Message;
                }
            }
            catch (Exception ex)
            {
                info.Code = "错误:" + ex.Message;
                info.StatusCode = HttpStatusCode.InternalServerError;
            }
            finally
            {
                if (responseStream != null)
                    responseStream.Close();
                if (response != null)
                    response.Close();
            }

            return info;
        }

        #region 取得远程资源
        /// <summary>
        /// 取得远程资源
        /// </summary>
        /// <param name="strUrl">要取的URL</param>
        /// <returns>网页源代码</returns>
        public RemoteRes GetRemoteResource(string strUrl)
        {
            HttpWebResponse response = null;
            Stream stream = null;
            RemoteRes info = new RemoteRes();
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUrl);
                request.AllowAutoRedirect = true;
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)";
                request.Referer = "http://" + new Uri(strUrl).Host;
                response = request.GetResponse() as HttpWebResponse;
                stream = response.GetResponseStream();
                info.ContentType = response.ContentType;
                if (this.ProxyHost.Length > 0)
                {
                    request.Proxy = new WebProxy(this.ProxyHost, this.ProxyInt);
                }
                else
                {
                    request.Proxy = null;
                }

                MemoryStream ms = new MemoryStream();

                byte[] buffer = new byte[256];

                int c = stream.Read(buffer, 0, buffer.Length);

                while (c > 0)
                {
                    ms.Write(buffer, 0, c);
                    c = stream.Read(buffer, 0, buffer.Length);
                }

                info.StatusCode = response.StatusCode;

                info.Bytes = ms.ToArray();

            }
            catch (WebException WE)
            {
                if (WE.Response != null)
                {
                    info.StatusCode = (WE.Response as HttpWebResponse).StatusCode;
                }
                else
                {
                    info.StatusCode = HttpStatusCode.ServiceUnavailable;
                }

                return info;
            }
            catch
            {
                info.StatusCode = HttpStatusCode.InternalServerError;

                return info;
            }
            finally
            {
                if (stream != null)
                    stream.Close();

                if (response != null)
                    response.Close();
            }
            return info;
        }
        #endregion


        public RemoteRes Post(string strUrl, string postData)
        {
            RemoteRes info = new RemoteRes();
            Stream responseStream = null;
            HttpWebResponse response = null;
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(postData);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUrl);
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = bytes.Length;
                request.Timeout = this.TimeOut;
                request.UserAgent = this.UserAgent;
                //request.Referer = string.Concat("http://", uri.Host);
                if (this.ProxyHost.Length > 0)
                {
                    request.Proxy = new WebProxy(this.ProxyHost, this.ProxyInt);
                }
                else
                {
                    request.Proxy = null;
                }
                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(bytes, 0, bytes.Length);
                }
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                    responseStream = response.GetResponseStream();

                    if (responseStream != null)
                    {
                        info.Html = new StreamReader(responseStream, Encoding.UTF8).ReadToEnd();
                    }
                    info.StatusCode = HttpStatusCode.OK;

                    return info;
                }
                catch (Exception ex)
                {
                    info.Html = "错误：" + ex.Message;
                }

            }
            catch (Exception ex)
            {
                info.Html = "错误：" + ex.Message;
            }
            finally
            {
                if (responseStream != null)
                    responseStream.Close();
                if (response != null)
                    response.Close();
            }
            return info;
        }

        #region 检查链接
        /// <summary>
        /// 检查链接是否存在
        /// </summary>
        /// <param name="strUrl"></param>
        public bool UrlExist(string strUrl)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(strUrl);
            request.Method = "HEAD";
            request.AllowAutoRedirect = false;
            request.Proxy = null;
            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 1.1.4322)";
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            if (response.StatusCode != HttpStatusCode.OK)
            {
                response.Close();
                return false;
            }
            else
            {
                return true;
            }


        }
        /// <summary>
        /// 检查死链接是否在能容忍的数量内
        /// </summary>
        /// <param name="URLs"></param>
        /// <param name="allowBadNum"></param>
        /// <returns></returns>
        public bool UrlExist(IList<string> URLs, int allowBadNum)
        {
            if (allowBadNum == 0)
            {
                return true;
            }
            //如果图片的数量小于能容忍的数量就不用检查了。
            if (URLs.Count <= allowBadNum)
            {
                return true;
            }
            int intTemp = 0;
            foreach (string strUrl in URLs.Where(strUrl => UrlExist(strUrl) == false))
            {
                intTemp++;
                if (intTemp > allowBadNum)
                {
                    return false;
                }
            }
            return true;
        }
        #endregion

        /// <summary>
        /// 检查网页是否更新
        /// </summary>
        /// <param name="url"></param>
        /// <param name="modifiedSince"></param>
        /// <returns></returns>
        public static bool PageHasChanged(string url, DateTime modifiedSince)
        {
            bool changed = false;

            // 设置请求信息
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            if (request == null)
            {
                return false;
            }
            // 最关键的一项设置 将请求的IfModifiedSince设置为指定的时间
            request.IfModifiedSince = modifiedSince;

            try
            {
                var response = request.GetResponse() as HttpWebResponse;
                if (response == null)
                {
                    return false;
                }
                // 根据返回的状态码判断内容是否发生了变化 
                // 200表示页面发生了变化
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    changed = true;
                }
                else if (response.StatusCode == HttpStatusCode.NotModified)
                {
                    changed = false;
                }

                response.Close();
            }
            catch (WebException ex)
            {
                // 对于没有发生变化的页面 通常会通过抛出异常的方式提示页面未发生变化
                if ((ex.Response as HttpWebResponse).StatusCode == HttpStatusCode.NotModified)
                {
                    changed = false;
                }
                else
                {
                    throw ex;
                }
            }

            return changed;
        }

        #region 获取编码
        /// <summary>
        /// 获取编码
        /// </summary>
        /// <param name="encodings"></param>
        /// <returns></returns>
        private static Encoding GetEncoding(string[] encodings)
        {
            try
            {
                if (encodings.Length == 1)
                {
                    if (encodings[0] == "nomatch")
                    {
                        return Encoding.GetEncoding("GBK");
                    }
                    else
                    {
                        return Encoding.GetEncoding(encodings[0]);
                    }
                }
                else
                {
                    string[] chinesecharsets = { "GB18030", "GBK", "GB2312", "Big5" };
                    var q = from x in chinesecharsets
                            where encodings.Contains(x.ToUpper())
                            select x;

                    if (q.Any() == false)
                    {
                        throw new NotSupportedException("没有中文编码，暂不采集。");
                    }
                    return Encoding.GetEncoding(q.First());
                }
            }
            catch
            {
                return null;
            }

        }

        #endregion
    }

    public class RemoteRes
    {
        /// <summary>
        /// 返回信息的代码
        /// </summary>
        public string Code { get; set; }

        /// <summary>
        /// 信息
        /// </summary>
        public string Html { get; set; }

        /// <summary>
        /// 远程资源
        /// </summary>
        public byte[] Bytes { get; set; }

        /// <summary>
        /// 内容类型
        /// </summary>
        public string ContentType { get; set; }

        /// <summary>
        /// 状态代码
        /// </summary>
        public HttpStatusCode StatusCode { get; set; }

        public string Charset { get; set; }

        public DateTime LastModified { get; set; }
    }
}